深入学习RTC时钟库 DS1302

您所在的位置:网站首页 arduino 1302时钟 深入学习RTC时钟库 DS1302

深入学习RTC时钟库 DS1302

2024-07-03 21:53:36| 来源: 网络整理| 查看: 265

❤️博主介绍❤️ 😬 作者:单片机菜鸟哥 👉火爆博客:ESP8266 Arduino教程

零基础从入门到熟悉Arduino平台下开发ESP8266,同时会涉及网络编程知识。专栏文章累计超过60篇,分为基础篇、网络篇、应用篇、高级篇,涵盖ESP8266大部分开发技巧。

👻最近更新:ESP32 MicroPython应用

讲解Python在esp32上的应用,包括网络请求、爬虫

快速导航 单片机菜鸟的博客快速索引(快速找到你要的)

如果觉得有用,麻烦点赞收藏,您的支持是博主创作的动力。

文章目录 1.前言2.RTC2.1 简介2.2 安装 3. DS1302 与 RTC库3.1 DS1302设计时钟芯片3.2 DS1302电路图与引脚关系3.3 DS1302具体操作3.3.1 DS1302寄存器3.3.2 RTCDS1302库3.3.3 RTCDS1302库函数3.3.3.1 Begin() —— 初始化3.3.3.2 GetIsWriteProtected() —— 判断是否写保护3.3.3.3 GetIsWriteProtected() —— 设置是否写保护3.3.3.4 GetIsRunning() —— 判断时钟是否正在运行3.3.3.5 SetIsRunning() —— 设置时钟是否运行3.3.3.6 SetDateTime() —— 设置日期时间3.3.3.7 GetDateTime() —— 获取日期时间3.3.3.8 SetMemory() —— 往RTC Memory写入数据3.3.3.9 GetMemory() —— 读取RTC Memory 3.3.3 DS1302接线3.3.4 测试用例3.3.4.1 测试时间3.3.4.2 测试RTC Memory 4.总结

1.前言

    最近博主在弄8266编程的时候,偶然发现两个全新时钟模块压仓货:

DS1302DS3231

    为了避免资源浪费以及重复编写代码,博主还是抱着尝试的心态去寻找能够同时兼容 DS1302、DS3231甚至其他的时钟模块的第三方库。终于,还是被我找到了 —— Rtc 时钟库。

2.RTC 2.1 简介 Arduino Real Time Clock library(Arduino平台的实时时钟库)支持DS1302、DS1307、DS3231、DS3234英文文档参考 wiki 2.2 安装 打开Arduino IDE的库管理器,然后搜索“Rtc by Makuna”,选择版本安装

image

为了更加了解该库的使用,博主会带着大家剖析源码。首先让我们看看代码结构:

在这里插入图片描述

    代码非常直接明了,分别支持了DS1302、DS1307、DS3231、DS3234。但是由于博主资源限制,本篇只会讨论DS1302以及DS3231,其他请读者自行学习。

3. DS1302 与 RTC库 3.1 DS1302设计时钟芯片

    网上介绍DS1302的帖子非常多,博主在这里就不从零开始讲解,这里推荐一个博主觉得写得还可以的参考文档 STM32与DS1302设计时钟芯片,超详细。     认真看完上面参考文档之后,博主得到几个我比较关心的点:

DS1302是一个实时时钟芯片,可以提供秒、分、小时、日期、月、年等信息,并且还有闰年自动调整的能力,可以通过配置AM/PM来决定采用24小时格式还是12小时格式;仅需用到三个口线:(1)RES 复位(2)I/O 数据线(3)SCLK串行时钟;拥有31字节数据存储RAM(RTC Memory);DS1302这种时钟芯片功耗一般都很低,它在工作电压2.0V的时候,工作电流小于300nA;DS1302的工作电压比较宽,大概是2.0V~5.5V都可以正常工作。采用双电源供电,当主电源比备用电源高0.2V时,由主电源供电,否则采用备用电源,一般是一个纽扣电池;读/写时钟或RAM 数据时有两种传送方式单字节传送和多字节传送字符组方式; 3.2 DS1302电路图与引脚关系

    接下来看看常用电路图以及引脚定义:

在这里插入图片描述

image

3.3 DS1302具体操作

    操作DS1302的大致过程,就是将各种数据或者控制命令写入DS1302的寄存器来完成对应操作(比如设置时间、写保护操作)。然后使DS1302开始运作,DS1302时钟会按照设置情况运转,再用单片机将其寄存器内的数据读出。再用液晶显示,就是我们常说的简易电子钟。

3.3.1 DS1302寄存器

    DS1302有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式,其日历、时间寄存器及其控制字见表。

image

    此外,DS1302 还有控制寄存器、充电寄存器、时钟突发寄存器及与RAM相关的寄存器等。时钟突发寄存器可一次性顺序读写除充电寄存器外的所有寄存器内容。

    DS1302与RAM相关的寄存器分为两类:一类是单个RAM单元,共31个,每个单元组态为一个8位的字节,其命令控制字为C0H~FDH,其中奇数为读操作,偶数为写操作;另一类为突发方式下的RAM寄存器,此方式下可一次性读写所有的RAM的31个字节,命令控制字为FEH(写)、FFH(读)。

3.3.2 RTCDS1302库

    上面只是介绍理论,在理解理论的基础上,我们开始讲解RTC库的使用。以下是RTCDS1302库的源码(参考源码注释):

#ifndef __RTCDS1302_H__ #define __RTCDS1302_H__ #include #include "RtcDateTime.h" #include "RtcUtility.h" //DS1302 Register Addresses 寄存器地址 const uint8_t DS1302_REG_TIMEDATE = 0x80;//秒寄存器 const uint8_t DS1302_REG_TIMEDATE_BURST = 0xBE;//时钟批量处理寄存器 const uint8_t DS1302_REG_TCR = 0x90;//涓流充电控制寄存器 const uint8_t DS1302_REG_RAM_BURST = 0xFE;//RAM批量处理寄存器 const uint8_t DS1302_REG_RAMSTART = 0xc0;//ram空间第一个字节 const uint8_t DS1302_REG_RAMEND = 0xfd;//ram空间最后一个字节 // ram read and write addresses are interleaved const uint8_t DS1302RamSize = 31;//ram空间大小 // DS1302 Trickle Charge Control Register Bits 以下跟涓流充电有关 可忽略 enum DS1302TcrResistor { DS1302TcrResistor_Disabled = 0, DS1302TcrResistor_2KOhm = B00000001, DS1302TcrResistor_4KOhm = B00000010, DS1302TcrResistor_8KOhm = B00000011, DS1302TcrResistor_MASK = B00000011, }; enum DS1302TcrDiodes { DS1302TcrDiodes_None = 0, DS1302TcrDiodes_One = B00000100, DS1302TcrDiodes_Two = B00001000, DS1302TcrDiodes_Disabled = B00001100, DS1302TcrDiodes_MASK = B00001100, }; enum DS1302TcrStatus { DS1302TcrStatus_Enabled = B10100000, DS1302TcrStatus_Disabled = B01010000, DS1302TcrStatus_MASK = B11110000, }; const uint8_t DS1302Tcr_Disabled = DS1302TcrStatus_Disabled | DS1302TcrDiodes_Disabled | DS1302TcrResistor_Disabled; // DS1302 Clock Halt Register & Bits const uint8_t DS1302_REG_CH = 0x80; // bit in the seconds register 秒寄存器 const uint8_t DS1302_CH = 7; // Write Protect Register & Bits const uint8_t DS1302_REG_WP = 0x8E; //写保护寄存器 const uint8_t DS1302_WP = 7; template class RtcDS1302 { public: RtcDS1302(T_WIRE_METHOD& wire) : _wire(wire) { } void Begin() { //会把三个引脚设置为输入状态 _wire.begin(); } /** * 判断是否写保护 * @return bool true表示写保护 */ bool GetIsWriteProtected() { //获取写保护寄存器的值 uint8_t wp = getReg(DS1302_REG_WP); //获取最高位的值 return !!(wp & _BV(DS1302_WP)); } /** * 设置是否写保护 * @param isWriteProtected * true 写保护 * false 去掉写保护 */ void SetIsWriteProtected(bool isWriteProtected) { //获取写保护寄存器的值 uint8_t wp = getReg(DS1302_REG_WP); if (isWriteProtected) { wp |= _BV(DS1302_WP); } else { wp &= ~_BV(DS1302_WP); } setReg(DS1302_REG_WP, wp); } bool IsDateTimeValid() { return GetDateTime().IsValid(); } /** * 判断时钟是否正在运行 * @return bool * true 时钟运行 * false 时钟停振,进入低功耗态 */ bool GetIsRunning() { uint8_t ch = getReg(DS1302_REG_CH); return !(ch & _BV(DS1302_CH)); } void SetIsRunning(bool isRunning) { uint8_t ch = getReg(DS1302_REG_CH); if (isRunning) { ch &= ~_BV(DS1302_CH); } else { ch |= _BV(DS1302_CH); } setReg(DS1302_REG_CH, ch); } uint8_t GetTrickleChargeSettings() { uint8_t setting = getReg(DS1302_REG_TCR); return setting; } void SetTrickleChargeSettings(uint8_t setting) { if ((setting & DS1302TcrResistor_MASK) == DS1302TcrResistor_Disabled) { // invalid resistor setting, set to disabled setting = DS1302Tcr_Disabled; goto apply; } if ((setting & DS1302TcrDiodes_MASK) == DS1302TcrDiodes_Disabled || (setting & DS1302TcrDiodes_MASK) == DS1302TcrDiodes_None) { // invalid diode setting, set to disabled setting = DS1302Tcr_Disabled; goto apply; } if ((setting & DS1302TcrStatus_MASK) != DS1302TcrStatus_Enabled) { // invalid status setting, set to disabled setting = DS1302Tcr_Disabled; goto apply; } apply: setReg(DS1302_REG_TCR, setting); } /** * 设置日期时间 * @param RtcDateTime 日期时间对象 */ void SetDateTime(const RtcDateTime& dt) { // set the date time 批量设置时间 _wire.beginTransmission(DS1302_REG_TIMEDATE_BURST); _wire.write(Uint8ToBcd(dt.Second()));//秒数 _wire.write(Uint8ToBcd(dt.Minute()));//分钟 _wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only _wire.write(Uint8ToBcd(dt.Day()));//天数 _wire.write(Uint8ToBcd(dt.Month()));//月份 // RTC Hardware Day of Week is 1-7, 1 = Monday // convert our Day of Week to Rtc Day of Week uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek()); _wire.write(Uint8ToBcd(rtcDow)); _wire.write(Uint8ToBcd(dt.Year() - 2000));//年份 _wire.write(0); // no write protect, as all of this is ignored if it is protected _wire.endTransmission();//批量写入寄存器 } /** * 获取日期时间 * @return RtcDateTime 日期时间对象 */ RtcDateTime GetDateTime() { // set the date time 批量获取时间 _wire.beginTransmission(DS1302_REG_TIMEDATE_BURST | THREEWIRE_READFLAG); uint8_t second = BcdToUint8(_wire.read() & 0x7F);//秒数 uint8_t minute = BcdToUint8(_wire.read());//分钟 uint8_t hour = BcdToBin24Hour(_wire.read());//小时 uint8_t dayOfMonth = BcdToUint8(_wire.read());//天数 uint8_t month = BcdToUint8(_wire.read());//月份 _wire.read(); // throwing away day of week as we calculate it uint16_t year = BcdToUint8(_wire.read()) + 2000;//年份 _wire.read(); // throwing away write protect flag _wire.endTransmission();//批量处理 //返回日期时间对象 return RtcDateTime(year, month, dayOfMonth, hour, minute, second); } /*** * 往RTC Memory写入数据 * @param memoryAddress 地址偏移量 * @param value 数据 */ void SetMemory(uint8_t memoryAddress, uint8_t value) { // memory addresses interleaved read and write addresses // so we need to calculate the offset uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART; if (address uint8_t value = 0; // memory addresses interleaved read and write addresses // so we need to calculate the offset uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART; if (address uint8_t countWritten = 0; //批量处理 _wire.beginTransmission(DS1302_REG_RAM_BURST); while (countBytes > 0 && countWritten uint8_t countRead = 0; _wire.beginTransmission(DS1302_REG_RAM_BURST | THREEWIRE_READFLAG); while (countBytes > 0 && countRead _wire.beginTransmission(regAddress | THREEWIRE_READFLAG); uint8_t regValue = _wire.read(); _wire.endTransmission(); return regValue; } void setReg(uint8_t regAddress, uint8_t regValue) { _wire.beginTransmission(regAddress); _wire.write(regValue); _wire.endTransmission(); } }; #endif // __RTCDS1302_H__ 3.3.3 RTCDS1302库函数 3.3.3.1 Begin() —— 初始化

函数说明:

/** * 初始化,会把三个引脚设置为输入状态 */ void Begin(); 3.3.3.2 GetIsWriteProtected() —— 判断是否写保护

函数说明:

/** * 判断是否写保护 * @return bool true表示写保护 */ bool GetIsWriteProtected();

源码说明:

bool GetIsWriteProtected() { //获取写保护寄存器的值 uint8_t wp = getReg(DS1302_REG_WP); //获取最高位的值 return !!(wp & _BV(DS1302_WP)); } 3.3.3.3 GetIsWriteProtected() —— 设置是否写保护

函数说明:

/** * 设置是否写保护 * @param isWriteProtected * true 写保护 * false 去掉写保护 */ void SetIsWriteProtected(bool isWriteProtected)

源码说明:

void SetIsWriteProtected(bool isWriteProtected) { //获取写保护寄存器的值 uint8_t wp = getReg(DS1302_REG_WP); if (isWriteProtected) { wp |= _BV(DS1302_WP); }else{ wp &= ~_BV(DS1302_WP); } setReg(DS1302_REG_WP, wp); } 3.3.3.4 GetIsRunning() —— 判断时钟是否正在运行

函数说明:

/** * 判断时钟是否正在运行 * @return bool * true 时钟运行 * false 时钟停振,进入低功耗态 */ bool GetIsRunning()

源码说明:

bool GetIsRunning() { //判断秒寄存器的最高位数值 uint8_t ch = getReg(DS1302_REG_CH); return !(ch & _BV(DS1302_CH)); } 3.3.3.5 SetIsRunning() —— 设置时钟是否运行

函数说明:

/** * 设置时钟是否运行 * @param isRunning * true 时钟运行 * false 时钟停振,进入低功耗态 */ void SetIsRunning(bool isRunning)

源码说明:

void SetIsRunning(bool isRunning) { uint8_t ch = getReg(DS1302_REG_CH); if (isRunning){ ch &= ~_BV(DS1302_CH); }else{ ch |= _BV(DS1302_CH); } //设置秒寄存器的最高位 setReg(DS1302_REG_CH, ch); } 3.3.3.6 SetDateTime() —— 设置日期时间

函数说明:

/** * 设置日期时间 * @param RtcDateTime 日期时间对象 */ void SetDateTime(const RtcDateTime& dt)

源码说明:

/** * 设置日期时间 * @param RtcDateTime 日期时间对象 */ void SetDateTime(const RtcDateTime& dt) { // set the date time 批量设置时间 _wire.beginTransmission(DS1302_REG_TIMEDATE_BURST); _wire.write(Uint8ToBcd(dt.Second()));//秒数 _wire.write(Uint8ToBcd(dt.Minute()));//分钟 _wire.write(Uint8ToBcd(dt.Hour())); // 24 hour mode only _wire.write(Uint8ToBcd(dt.Day()));//天数 _wire.write(Uint8ToBcd(dt.Month()));//月份 // RTC Hardware Day of Week is 1-7, 1 = Monday // convert our Day of Week to Rtc Day of Week uint8_t rtcDow = RtcDateTime::ConvertDowToRtc(dt.DayOfWeek()); _wire.write(Uint8ToBcd(rtcDow)); _wire.write(Uint8ToBcd(dt.Year() - 2000));//年份 _wire.write(0); // no write protect, as all of this is ignored if it is protected _wire.endTransmission();//批量写入寄存器 }

参数说明:

RtcDateTime RTC日期时间对象 class RtcDateTime { public: RtcDateTime(uint32_t secondsFrom2000 = 0); RtcDateTime(uint16_t year, uint8_t month, uint8_t dayOfMonth, uint8_t hour, uint8_t minute, uint8_t second) : _yearFrom2000((year >= c_OriginYear) ? year - c_OriginYear : year), _month(month), _dayOfMonth(dayOfMonth), _hour(hour), _minute(minute), _second(second) { } // RtcDateTime compileDateTime(__DATE__, __TIME__); RtcDateTime(const char* date, const char* time); bool IsValid() const; uint16_t Year() const { return c_OriginYear + _yearFrom2000; } uint8_t Month() const { return _month; } uint8_t Day() const { return _dayOfMonth; } uint8_t Hour() const { return _hour; } uint8_t Minute() const { return _minute; } uint8_t Second() const { return _second; } // 0 = Sunday, 1 = Monday, ... 6 = Saturday uint8_t DayOfWeek() const; // 32-bit times as seconds since 1/1/2000 uint32_t TotalSeconds() const; uint64_t TotalSeconds64() const; // add seconds void operator += (uint32_t seconds) { RtcDateTime after = RtcDateTime( TotalSeconds() + seconds ); *this = after; } // remove seconds void operator -= (uint32_t seconds) { RtcDateTime before = RtcDateTime( TotalSeconds() - seconds ); *this = before; } // allows for comparisons to just work (==, , =, !=) operator uint32_t() const { return TotalSeconds(); } // Epoch32 support uint32_t Epoch32Time() const { return TotalSeconds() + c_Epoch32OfOriginYear; } void InitWithEpoch32Time(uint32_t time) { _initWithSecondsFrom2000(time - c_Epoch32OfOriginYear); } // Epoch64 support uint64_t Epoch64Time() const { return TotalSeconds64() + c_Epoch32OfOriginYear; } void InitWithEpoch64Time(uint64_t time) { _initWithSecondsFrom2000(time - c_Epoch32OfOriginYear); } void InitWithIso8601(const char* date); // convert our Day of Week to Rtc Day of Week // RTC Hardware Day of Week is 1-7, 1 = Monday static uint8_t ConvertDowToRtc(uint8_t dow) { if (dow == 0) { dow = 7; } return dow; } // convert Rtc Day of Week to our Day of Week static uint8_t ConvertRtcToDow(uint8_t rtcDow) { return (rtcDow % 7); } protected: uint8_t _yearFrom2000; uint8_t _month; uint8_t _dayOfMonth; uint8_t _hour; uint8_t _minute; uint8_t _second; template void _initWithSecondsFrom2000(T secondsFrom2000) { _second = secondsFrom2000 % 60; T timeFrom2000 = secondsFrom2000 / 60; _minute = timeFrom2000 % 60; timeFrom2000 /= 60; _hour = timeFrom2000 % 24; T days = timeFrom2000 / 24; T leapDays; for (_yearFrom2000 = 0;; ++_yearFrom2000) { leapDays = (_yearFrom2000 % 4 == 0) ? 1 : 0; if (days // set the date time 批量获取时间 _wire.beginTransmission(DS1302_REG_TIMEDATE_BURST | THREEWIRE_READFLAG); uint8_t second = BcdToUint8(_wire.read() & 0x7F);//秒数 uint8_t minute = BcdToUint8(_wire.read());//分钟 uint8_t hour = BcdToBin24Hour(_wire.read());//小时 uint8_t dayOfMonth = BcdToUint8(_wire.read());//天数 uint8_t month = BcdToUint8(_wire.read());//月份 _wire.read(); // throwing away day of week as we calculate it uint16_t year = BcdToUint8(_wire.read()) + 2000;//年份 _wire.read(); // throwing away write protect flag _wire.endTransmission();//批量处理 //返回日期时间对象 return RtcDateTime(year, month, dayOfMonth, hour, minute, second); } 3.3.3.8 SetMemory() —— 往RTC Memory写入数据

函数说明:

/*** * 往RTC Memory写入数据 * @param memoryAddress 地址偏移量 * @param value 数据 */ void SetMemory(uint8_t memoryAddress, uint8_t value) /** * 往RTC Memory批量写入数据 * @param pValue 批量数据 * @param countBytes 数据字节数 */ uint8_t SetMemory(const uint8_t* pValue, uint8_t countBytes)

源码说明:

void SetMemory(uint8_t memoryAddress, uint8_t value) { // memory addresses interleaved read and write addresses // so we need to calculate the offset uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART; if (address uint8_t countWritten = 0; //批量处理 _wire.beginTransmission(DS1302_REG_RAM_BURST); while (countBytes > 0 && countWritten uint8_t value = 0; // memory addresses interleaved read and write addresses // so we need to calculate the offset uint8_t address = memoryAddress * 2 + DS1302_REG_RAMSTART; if (address uint8_t countRead = 0; _wire.beginTransmission(DS1302_REG_RAM_BURST | THREEWIRE_READFLAG); while (countBytes > 0 && countRead 5 // DS1302 DAT/IO --> 6 // DS1302 RST/CE --> 7 // DS1302 VCC --> 3.3v - 5v // DS1302 GND --> GND #include #include ThreeWire myWire(/*IO*/6,/*SCLK*/5,/*CE*/7); // IO, SCLK, CE RtcDS1302 Rtc(myWire); void setup () { Serial.begin(57600); Serial.print("compiled: "); Serial.print(__DATE__); Serial.println(__TIME__); Rtc.Begin(); RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); printDateTime(compiled); Serial.println(); if (Rtc.GetIsWriteProtected()) { Serial.println("RTC was write protected, enabling writing now"); Rtc.SetIsWriteProtected(false); } if (!Rtc.GetIsRunning()) { Serial.println("RTC was not actively running, starting now"); Rtc.SetIsRunning(true); } RtcDateTime now = Rtc.GetDateTime(); if (now Serial.println("RTC is newer than compile time. (this is expected)"); } else if (now == compiled) { Serial.println("RTC is the same as compile time! (not expected but all is fine)"); } } void loop () { RtcDateTime now = Rtc.GetDateTime(); printDateTime(now); Serial.println(); delay(10000); // ten seconds } #define countof(a) (sizeof(a) / sizeof(a[0])) void printDateTime(const RtcDateTime& dt) { char datestring[20]; snprintf_P(datestring, countof(datestring), PSTR("%02u/%02u/%04u %02u:%02u:%02u"), dt.Month(), dt.Day(), dt.Year(), dt.Hour(), dt.Minute(), dt.Second() ); Serial.print(datestring); }

实验结果: image

3.3.4.2 测试RTC Memory

实验内容

往RTC Memory存入“danpianjicainiao”读取存储内容并打印到串口显示

实验器材

Mega2560 + DS1302

引脚连接

模块引脚Mega2560引脚VCCVCCGNDGNDDAT6CLK5RST7

实验代码

// CONNECTIONS: // DS1302 CLK/SCLK --> 5 // DS1302 DAT/IO --> 6 // DS1302 RST/CE --> 7 // DS1302 VCC --> 3.3v - 5v // DS1302 GND --> GND #include #include ThreeWire myWire(/*IO*/6,/*SCLK*/5,/*CE*/7); // IO, SCLK, CE RtcDS1302 Rtc(myWire); #define countof(a) (sizeof(a) / sizeof(a[0])) const char data[] = "danpianjicainiao"; void setup () { Serial.begin(57600); Serial.print("compiled: "); Serial.print(__DATE__); Serial.println(__TIME__); Rtc.Begin(); RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__); printDateTime(compiled); Serial.println(); if (Rtc.GetIsWriteProtected()) { Serial.println("RTC was write protected, enabling writing now"); Rtc.SetIsWriteProtected(false); } if (!Rtc.GetIsRunning()) { Serial.println("RTC was not actively running, starting now"); Rtc.SetIsRunning(true); } RtcDateTime now = Rtc.GetDateTime(); if (now Serial.print("something didn't match, count = "); Serial.print(count, DEC); Serial.print(", written = "); Serial.print(written, DEC); Serial.println(); } /* end of comment out section */ } void loop () { RtcDateTime now = Rtc.GetDateTime(); printDateTime(now); Serial.println(" +"); delay(5000); // read data uint8_t buff[20]; const uint8_t count = sizeof(buff); // get our data uint8_t gotten = Rtc.GetMemory(buff, count); if (gotten != count) { Serial.print("something didn't match, count = "); Serial.print(count, DEC); Serial.print(", gotten = "); Serial.print(gotten, DEC); Serial.println(); } Serial.print("data read ("); Serial.print(gotten); Serial.print(") = \""); // print the string, but terminate if we get a null for (uint8_t ch = 0; ch char datestring[20]; snprintf_P(datestring, countof(datestring), PSTR("%02u/%02u/%04u %02u:%02u:%02u"), dt.Month(), dt.Day(), dt.Year(), dt.Hour(), dt.Minute(), dt.Second() ); Serial.print(datestring); }

实验结果: 在这里插入图片描述

4.总结

本博文讲解如何使用RTC时钟库来操作DS1302,同时附带源码解析,请关注下一篇DS3231.



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭